﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace GraphBase.Presentation
{
    /// <summary>
    /// 几何运算的类
    /// </summary>
    public class Geometry
    {
        /// <summary>
        /// 根据圆上三点：起始点、终点、中点，高比求圆心
        /// </summary>
        /// <param name="startpoint">圆弧起始点</param>
        /// <param name="endpoint">圆弧终点</param>
        /// <param name="middlepoint">圆弧上的中点</param>
        /// <param name="arcd">圆弧上的中点到连接起始点终点直线的距离</param>
        /// <returns>圆心point_o</returns>
        public static PointF getPoint_O(PointF startpoint, PointF endpoint, PointF middlepoint, float arcd)
        {
            PointF point_o = new PointF();
            try
            {
                float d = getDirection(startpoint, endpoint);//起始点终点的距离
                float arc_r = (d * d + (4 * arcd * arcd)) / (8 * arcd);//半径

                if ((startpoint.Y == endpoint.Y)&&(startpoint.X<endpoint.X))//起始点在终点的左边
                {
                    //startpoint,endpoint两点之间的距离
                    point_o.X = middlepoint.X;
                    point_o.Y = middlepoint.Y + arc_r;
                }
                if ((startpoint.Y == endpoint.Y) && (startpoint.X > endpoint.X))//起始点在终点的右边
                {
                    point_o.X = middlepoint.X;
                    point_o.Y = middlepoint.Y - arc_r;
                }
                else
                {
                    float b = ((endpoint.Y * endpoint.Y) - (startpoint.Y * startpoint.Y) + (endpoint.X * endpoint.X) - (startpoint.X * startpoint.X)) / ((endpoint.Y - startpoint.Y) * 2);
                    float k = (endpoint.X - startpoint.X) / (endpoint.Y - startpoint.Y);
                    float chushu = (middlepoint.X * middlepoint.X) + (middlepoint.Y * middlepoint.Y) - (endpoint.X * endpoint.X) - (endpoint.Y * endpoint.Y) - (2 * b * (middlepoint.Y - endpoint.Y));
                    float beichushu = 2 * ((middlepoint.X - endpoint.X) - (k * (middlepoint.Y - endpoint.Y)));
                    point_o.X = chushu / beichushu;
                    point_o.Y = b - (k * point_o.X);
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.ToString());
            }
            return point_o;
        }
        /// <summary>
        /// 两点之间的距离
        /// </summary>
        /// <param name="A">一个点</param>
        /// <param name="B">另一个点</param>
        /// <returns>距离d</returns>
        public static float getDirection(PointF A, PointF B)
        {
            float d;
            d = (float)Math.Sqrt(((A.X - B.X) * (A.X - B.X)) + ((A.Y - B.Y) * (A.Y - B.Y)));
            return d;
        }
        /// <summary>
        /// 根据初始点、终点和弧上中点与初始点和终点的中点的距离求弧上中点
        /// 主要是求解一元二次方程，注意有两个解时如何取值
        /// </summary>
        /// <param name="startpoint">圆弧初始节点</param>
        /// <param name="endpoint">圆弧终点</param>
        /// <param name="arcd">弧上中点与初始点和终点的中点的距离</param>
        /// <returns>弧上中点middlept</returns>
        public static PointF getArcMiddlePoint(PointF startpoint, PointF endpoint, float arcd)
        {
            //if (arcd < -0.6f || arcd > 0.6f)
            //    throw new ArgumentOutOfRangeException("ArcHeightRadio should between -0.6 to 0.6");
            //求解一元二次方程ex^2+fx+g=0
            PointF m_line = new PointF();//初始点终点的中点
            PointF middlept = new PointF();//弧上的中点
            m_line.X = (startpoint.X + endpoint.X) / 2;
            m_line.Y = (startpoint.Y + endpoint.Y) / 2;
            //直线方程y=kx+b,这里k值取了相反数
            float b = ((endpoint.Y * endpoint.Y) - (startpoint.Y * startpoint.Y) + (endpoint.X * endpoint.X) - (startpoint.X * startpoint.X)) / (float)((endpoint.Y - startpoint.Y) * 2);
            float k = (float)(endpoint.X - startpoint.X) / (float)(endpoint.Y - startpoint.Y);
            float e = k * k + 1;//x^2前面的系数
            float f = (2 * k * m_line.Y) - (2 * k * b) - (2 * m_line.X);//x前面的系数
            float g = (m_line.X * m_line.X) + (m_line.Y * m_line.Y) - (2 * b * m_line.Y) + (b * b) - ( arcd* arcd);
            float T = 0;
            float x1 = 0;
            float x2 = 0;
            try
            {
                if (e == 0)
                {
                    return middlept;
                }
                else
                {
                    T = (f * f) - (4 * e * g);
                    if (T == 0)//即弧就是连接初始点终点的直线，基本不存在这样的情况
                    {
                        x1 = x2 = (-f) / (2 * e);
                        middlept.X = x1;
                    }
                    if (T > 0)//两个值的情况
                    {
                        x1 = ((-f) + (float)Math.Sqrt(T)) / (2 * e);
                        x2 = ((-f) - (float)Math.Sqrt(T)) / (2 * e);
                        if ((((startpoint.X <= endpoint.X) && (startpoint.Y < endpoint.Y)))||(((startpoint.X > endpoint.X) && (startpoint.Y < endpoint.Y))))//起始点在终点的右上角、左上角或正上方 x取大的值
                        {
                            if (x1 > x2)
                            {
                                middlept.X = x1;
                            }
                            else
                                middlept.X = x2;
                        }
                        if ((((startpoint.X > endpoint.X) && (startpoint.Y > endpoint.Y)))||(((startpoint.X <= endpoint.X) && (startpoint.Y > endpoint.Y))))//起始点在终点的右下角、左下角或正下方 x取小的值
                        {
                            if (x1 > x2)
                            {
                                middlept.X = x2;
                            }
                            else
                                middlept.X = x1;
                        }
                    }
                    middlept.Y = b - k * middlept.X;
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.ToString());
            }
            return middlept;
        }
        /// <summary>
        /// 判断点是否在线段上
        /// 分别计算当前点与初始点和终点的距离SC、EC
        /// 将两者加起来，与初始点和终点间的距离SE做比较，相符的表示在线上，反之不在线上
        /// </summary>
        /// <param name="startpoint">开始点</param>
        /// <param name="endpoint">终点</param>
        /// <param name="currentPoint">当前鼠标点中的位置</param>
        /// <returns>返回布尔类型</returns>
        public static bool IsPointInLine(PointF startpoint, PointF endpoint, PointF currentPoint)
        {
            float SE=getDirection(startpoint,endpoint);//初始节点与终点之间的距离
            float SC=getDirection(startpoint,currentPoint);//当前点与起始点的距离
            float EC = getDirection(endpoint, currentPoint);//当前点与终点的距离
            float SCEC=SC+EC;
            float cha=Math.Abs(SCEC-SE);
            if ((startpoint.X == endpoint.X) && (startpoint.Y == endpoint.Y))//已知两点重复
                return false;
            else if (startpoint.X == endpoint.X)//与X轴垂直
            {
                if (currentPoint.X == startpoint.X)
                    return true;
                else
                    return false;
            }
            else if (startpoint.Y == currentPoint.Y)//与X轴平行
            {
                if (currentPoint.Y == startpoint.Y)
                    return true;
                else
                    return false;
            }
            else
            {
                if ((cha >= 0) && (cha <= 1))
                    return true;
                else
                    return false;
            }
        }
        /// <summary>
        /// 判断是否在圆弧上
        /// 根据弧长
        /// 分别计算当前点与初始点的弧长SC_Arc、当前点与终点的弧长EC_Arc
        /// 将两者加起来，与初始点和终点之间的弧长SE_Arc做比较，相符则表示点在弧上，反之不在弧上
        /// </summary>
        /// <param name="startpoint">初始点</param>
        /// <param name="endpoint">终点</param>
        /// <param name="currentPoint">当前点</param>
        /// <param name="arcd">圆弧上中点到初始点终点的中点的距离</param>
        /// <returns>布尔类型</returns>
        public static bool IsPointInArc(PointF startpoint, PointF endpoint, PointF currentPoint,PointF Point_O, float arcd)
        {
            //弧的程度长度与两点间的距离有直接关系，
            //1，求出两点距离d
            //2，求出弧对应的圆心角=2arcsin(d/2r)
            //3,求弧长=2rarcsin(d/2r)
            float OC = getDirection(currentPoint, Point_O);//圆心到当前点的距离
            float SE = getDirection(startpoint, endpoint);//初始节点与终点之间的距离
            float SC = getDirection(startpoint, currentPoint);//当前点与起始点的距离
            float EC = getDirection(endpoint, currentPoint);//当前点与终点的距离
            float r = (SE * SE + (4 * arcd * arcd)) / (8 * arcd);//半径
            float SE_anglecentral = 2 * (float)Math.Asin(SE / (2 * r));//SE之间的圆心角
            float SC_anglecentral = 2 * (float)Math.Asin(SC / (2 * r));//SA之间的圆心角
            float EC_anglecentral = 2 * (float)Math.Asin(EC / (2 * r));//EA之间的圆心角
            float SE_Arc = 2 * r * (float)Math.Asin(SE / (2 * r));//SE之间的弧长
            float SC_Arc = 2 * r * (float)Math.Asin(SC / (2 * r));//SA之间的弧长
            float EC_Arc = 2 * r * (float)Math.Asin(EC / (2 * r));//EA之间的弧长
            float SCEC = SC_Arc + EC_Arc;
            float cha = Math.Abs(SE_Arc - SCEC);//弧长比较
            float radiuscha = Math.Abs(OC - r);//半径比较
            if ((startpoint.X == endpoint.X) && (startpoint.Y == endpoint.Y))//已知两点重复
                return false;
            else if (startpoint.X == endpoint.X)//与X轴垂直
            {
                if (currentPoint.X == startpoint.X)
                    return true;
                else
                    return false;
            }
            else if (startpoint.Y == currentPoint.Y)//与X轴平行
            {
                if (currentPoint.Y == startpoint.Y)
                    return true;
                else
                    return false;
            }
            else
            {
                if ((cha >= 0) && (cha <= 3)&&(radiuscha>=0)&&(radiuscha<=3))
                    return true;
                else
                    return false;
            }
        }
        //计算初始边与X轴的向量夹角:初始点，终点，圆心
        /// <summary>
        /// 计算初始边与X轴正方向向量的夹角
        /// </summary>
        /// <param name="spt">初始点</param>
        /// <param name="ept">终点</param>
        /// <param name="opt">圆心</param>
        /// <returns>角度startAngle</returns>
        public static float Start_Angle(PointF spt,PointF ept,PointF opt)
        {
            //x轴上的向量
            float Vector_Xx = spt.X;
            float Vector_Xy = 0;
            //Os向量
            float Vector_sx = spt.X - opt.X;
            float Vector_sy = spt.Y - opt.Y;
            //求出几何坐标系中的角度，即按逆时针的方法
            float s_Angle=0;
            //初始角度
            float startAngle = 0;
            try
            {
                //os与x轴上向量的点乘积
                float Point_Mul_s = (Vector_sx * Vector_Xx) + (Vector_sy * Vector_Xy);
                float Mul_s = (float)Math.Sqrt(Vector_sx * Vector_sx + Vector_sy * Vector_sy) * (float)Math.Sqrt(Vector_Xx * Vector_Xx + Vector_Xy * Vector_Xy);
                //os与x轴夹角余弦值
                float Cos_s = Point_Mul_s / Mul_s;
                float S_Cos = (float)Math.Acos(Cos_s);//弧度值：弧度=角度值*（(float)Math.PI/180）
                //根据公式求出几何坐标系中的角度，即按逆时针的方法
                s_Angle = S_Cos * (180 / (float)Math.PI);



                if (((spt.X <= ept.X) && (spt.Y < ept.Y)))//起始点在终点的左上角
                {
                    startAngle = -s_Angle;
                }
                if (((spt.X > ept.X) && (spt.Y < ept.Y))||((spt.X < ept.X) && (spt.Y > ept.Y)))//起始点在终点的右上角或左下角
                {
                    if (opt.Y <= spt.Y)
                    {
                        startAngle = s_Angle;
                    }
                    else
                    {
                        startAngle = -s_Angle;
                    }
                }
                if (((spt.X >= ept.X) && (spt.Y > ept.Y)))//起始点在终点的右下角或正下方
                {
                    startAngle = s_Angle;
                }
                if ((spt.Y == ept.Y) && (spt.X < ept.X))//起始点在终点的左边
                {
                    startAngle = -s_Angle;
                }
                if ((spt.Y == ept.Y) && (spt.X > ept.X))//起始点在终点的右边边
                {
                    startAngle = s_Angle;
                }
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.ToString());
            }
            return startAngle;
        }
        //计算圆心角
        public static float AngleCentral(PointF spt, PointF ept,float arcd)
        {
            float anglecentral = 0;
            PointF mpt=new PointF();
            float a_sin=0;
            mpt.X = (spt.X + ept.X) / 2;
            mpt.Y = (spt.Y + ept.Y) / 2;
            try
            {
                float SE = getDirection(spt, ept);//初始节点与终点之间的距离
                //float d = getDirection(A, B);//起始点终点的距离
                float arc_r = (SE * SE + (4 * arcd * arcd)) / (8 * arcd);//半径
                a_sin = 2 * (float)Math.Asin(SE / (2 * arc_r));//SE之间的圆心角的弧度值：弧度=角度值*（(float)Math.PI/180）
                anglecentral = a_sin * (180 / (float)Math.PI);//将弧度值转换成角度
            }
            catch (Exception exp)
            {
                MessageBox.Show(exp.ToString());
            }
            return anglecentral;
        }
        ////计算终点边与X轴向量的夹角：初始点，终点，圆心
        //public static float E_Angle(PointF S, PointF E, PointF O)
        //{
        //    //x轴上的向量
        //    float Vector_Xx = S.X;
        //    float Vector_Xy = 0;
        //    //oe向量
        //    float Vector_ex = E.X - O.X;
        //    float Vector_ey = E.Y - O.Y;
        //    //求出几何坐标系中的角度，即按逆时针的方法
        //    float e_Angle = 0;
        //    try
        //    {
        //        //oe和X轴上向量的点乘积
        //        float Point_Mul_e = (Vector_ex * Vector_Xx) + (Vector_ey * Vector_Xy);
        //        float Mul_e = (float)Math.Sqrt(Vector_ex * Vector_ex + Vector_ey * Vector_ey) * (float)Math.Sqrt(Vector_Xx * Vector_Xx + Vector_Xy * Vector_Xy);
        //        //oe与x轴夹角余弦值
        //        float Cos_e = Point_Mul_e / Mul_e;
        //        float E_Cos = (float)Math.Acos(Cos_e);
        //        //求出几何坐标系中的角度，即按逆时针的方法 
        //        e_Angle = E_Cos * (180 / (float)Math.PI);
        //    }
        //    catch (Exception exp)
        //    {
        //        MessageBox.Show(exp.ToString());
        //    }
        //    return e_Angle;
        //}
    }
}
